﻿using System;
using System.Linq;
using System.Collections.Generic;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using SuperGo.SilverlightLib;

namespace SuperGo.TestSilverlightLib {
	[TestClass]
	public class GoGameTest {
		private GoGame _game;
		private bool moveMadeFired;

		[TestInitialize]
		public void TestInitialize() {
			_game = new GoGame(9, 0, 5.5m, "white username", "black username");
		}

		[TestMethod]
		public void TestIsSuicideCorner() {
			bool result = _game.IsValidMove(Player.White, 0, 0);
			Assert.AreEqual(true, result);
		}

		[TestMethod]
		public void TestIsSuicideNoOneAround() {
			bool result = _game.IsValidMove(Player.White, 4, 4);
			Assert.AreEqual(true, result);
		}

		[TestMethod]
		public void TestIsSuicideSurroundedCorner() {
			_game.Board[1, 0].State = StoneState.Black;
			_game.Board[0, 1].State = StoneState.Black;
			bool result = _game.IsValidMove(Player.White, 0, 0);
			Assert.AreEqual(false, result);
		}

		[TestMethod]
		public void TestIsSuicideSurroundedMiddle() {
			_game.Board[3, 2].State = StoneState.Black;
			_game.Board[3, 4].State = StoneState.Black;
			_game.Board[2, 3].State = StoneState.Black;
			_game.Board[4, 3].State = StoneState.Black;
			bool result = _game.IsValidMove(Player.White, 3, 3);
			Assert.AreEqual(false, result);
		}

		/// <summary>
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . O O . . . . .
		/// . . X X O . . . .
		/// . . O O . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestPlayCaptureMiddle() {
			_game.Board[2, 2].State = StoneState.White;
			_game.Board[3, 2].State = StoneState.White;
			_game.Board[4, 3].State = StoneState.White;
			_game.Board[2, 4].State = StoneState.White;
			_game.Board[3, 4].State = StoneState.White;
			_game.Board[2, 3].State = StoneState.Black;
			_game.Board[3, 3].State = StoneState.Black;
			_game.Pass();
			_game.Play(1, 3);
			Assert.AreEqual(StoneState.Empty, _game.Board[2, 3].State);
			Assert.AreEqual(StoneState.Empty, _game.Board[3, 3].State);
		}

		/// <summary>
		/// X X . X . . . . .
		/// O O X . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestPlayCaptureCorner() {
			_game.Board[0, 0].State = StoneState.Black;
			_game.Board[1, 0].State = StoneState.Black;
			_game.Board[3, 0].State = StoneState.Black;
			_game.Board[2, 1].State = StoneState.Black;
			_game.Board[0, 1].State = StoneState.White;
			_game.Board[1, 1].State = StoneState.White;
			_game.Pass();
			_game.Play(2, 0);
			Assert.AreEqual(StoneState.Empty, _game.Board[0, 0].State);
			Assert.AreEqual(StoneState.Empty, _game.Board[1, 0].State);
			Assert.AreEqual(StoneState.Black, _game.Board[3, 0].State);
			Assert.AreEqual(StoneState.Black, _game.Board[2, 1].State);
		}

		[TestMethod]
		public void TestPass() {
			Assert.AreEqual(GameState.BlackToMove, _game.GameState);
			_game.Pass();
			Assert.AreEqual(GameState.WhiteToMove, _game.GameState);
			_game.Pass();
			Assert.AreEqual(GameState.NegotiatingDeadStones, _game.GameState);
			try {
				_game.Pass();
				Assert.Fail("Should have thrown an exception");
			} catch (GoException) {
				// Good.
			}
		}

		[TestMethod]
		public void TestPassResetLastMovePassFlag() {
			Assert.AreEqual(GameState.BlackToMove, _game.GameState);
			_game.Pass();
			Assert.AreEqual(GameState.WhiteToMove, _game.GameState);
			_game.Play(1, 1);
			Assert.AreEqual(GameState.BlackToMove, _game.GameState);
			_game.Pass();
			Assert.AreEqual(GameState.WhiteToMove, _game.GameState);
		}

		/// <summary>
		/// . O . O X . . . .
		/// . . O X . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestKo() {
			if (_game.CurrentPlayer == Player.Black) {
				_game.Pass();
			}

			_game.Play(1, 0); // white
			_game.Play(4, 0); // black
			_game.Play(3, 0); // white
			_game.Play(3, 1); // black
			_game.Play(2, 1); // white

			_game.Play(2, 0); // black

			Console.WriteLine(_game.Board.ToString());
			bool result = _game.IsValidMove(3, 0); // white
			Assert.AreEqual(false, result);
			Console.WriteLine(_game.Board.ToString());
			Assert.AreEqual(StoneState.White, _game.Board[1, 0].State);
			Assert.AreEqual(StoneState.White, _game.Board[2, 1].State);
			Assert.AreEqual(StoneState.Black, _game.Board[2, 0].State);
			Assert.AreEqual(StoneState.Black, _game.Board[4, 0].State);
			Assert.AreEqual(StoneState.Black, _game.Board[3, 1].State);
		}

		[TestMethod]
		public void TestResignThenCheckBoardState() {
			_game.Resign();
			_game.IsValidMove(0, 0);
		}

		[TestMethod]
		public void TestMoveMadeEvent_Play() {
			_game.MoveMade += new GoGame.MoveMadeDelegate(game_MoveMade);
			_game.Play(0, 0);
			Assert.IsTrue(moveMadeFired);
		}

		[TestMethod]
		public void TestMoveMadeEvent_Pass() {
			bool passFired = false;
			_game.PlayerPassed += delegate(GoGame source, Player player) {
				Assert.AreEqual(Player.Black, player);
				passFired = true;
			};
			_game.Pass();
			Assert.IsTrue(passFired);
		}

		void game_MoveMade(GoGame source, int x, int y, Player player, int captures) {
			moveMadeFired = true;
		}

		/// <summary>
		/// X X X X . . . . .
		/// O O X . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestGetGroup_Black() {
			_game.Board[0, 0].State = StoneState.Black;
			_game.Board[1, 0].State = StoneState.Black;
			_game.Board[2, 0].State = StoneState.Black;
			_game.Board[3, 0].State = StoneState.Black;
			_game.Board[2, 1].State = StoneState.Black;
			_game.Board[0, 1].State = StoneState.White;
			_game.Board[1, 1].State = StoneState.White;

			IEnumerable<Intersection> group = _game.GetGroup(0, 0).ToList();

			Assert.AreEqual(5, group.Count());
			Assert.AreEqual(1, group.Count(i => i.X == 0 && i.Y == 0));
			Assert.AreEqual(1, group.Count(i => i.X == 1 && i.Y == 0));
			Assert.AreEqual(1, group.Count(i => i.X == 2 && i.Y == 0));
			Assert.AreEqual(1, group.Count(i => i.X == 3 && i.Y == 0));
			Assert.AreEqual(1, group.Count(i => i.X == 2 && i.Y == 1));
		}

		/// <summary>
		/// X X X X . . . . .
		/// O O X . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestGetGroup_Empty() {
			_game.Board[0, 0].State = StoneState.Black;
			_game.Board[1, 0].State = StoneState.Black;
			_game.Board[2, 0].State = StoneState.Black;
			_game.Board[3, 0].State = StoneState.Black;
			_game.Board[2, 1].State = StoneState.Black;
			_game.Board[0, 1].State = StoneState.White;
			_game.Board[1, 1].State = StoneState.White;

			List<Intersection> group = _game.GetGroup(3, 3).ToList();

			Assert.AreEqual(74, group.Count());
		}

		/// <summary>
		/// X X X X . . . . .
		/// O O X . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestMarkAsDead() {
			_game.Play(0, 0); // black
			_game.Play(0, 1); // white
			_game.Play(1, 0); // black
			_game.Play(1, 1); // white
			_game.Play(2, 0); // black
			_game.Pass(); // white
			_game.Play(3, 0); // black
			_game.Pass(); // white
			_game.Play(2, 1); // black
			_game.Pass();
			_game.Pass();

			int deadStonesMarkedCalled = 0;

			_game.DeadStonesMarked += delegate(GoGame source, IList<Intersection> deadStones) {
				Assert.IsTrue(deadStones.Count == 5 || deadStones.Count == 2);
				deadStonesMarkedCalled++;
			};

			// mark all the X's as dead
			_game.Play(1, 0);

			// assert that the DeadStonesMarked event is called once
			Assert.AreEqual(1, deadStonesMarkedCalled);

			int captureMadeCalled = 0;
			_game.CaptureMade += delegate(GoGame source, int x, int y) {
				Assert.IsTrue(x < 4);
				Assert.IsTrue(y < 2);
				captureMadeCalled++;
			};

			// mark all the O's as dead too
			_game.Play(1, 1);

			// ensure there are no captures so far
			Assert.AreEqual(0, _game.BlackCaptures);
			Assert.AreEqual(0, _game.WhiteCaptures);

			// merge the stones marked as dead back into the board state
			_game.MergeStonesMarkedAsDead();

			Assert.AreEqual(7, captureMadeCalled);
			Assert.AreEqual(2, _game.BlackCaptures);
			Assert.AreEqual(5, _game.WhiteCaptures);
		}

		/// <summary>
		/// X X X X . . . . .
		/// O O X . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// . . . . . . . . .
		/// </summary>
		[TestMethod]
		public void TestResetDeadGroups() {
			_game.Play(0, 0); // black
			_game.Play(0, 1); // white
			_game.Play(1, 0); // black
			_game.Play(1, 1); // white
			_game.Play(2, 0); // black
			_game.Pass(); // white
			_game.Play(3, 0); // black
			_game.Pass(); // white
			_game.Play(2, 1); // black
			_game.Pass();
			_game.Pass();

			// this shouldn't do anything
			_game.ResetDeadGroups();

			// mark all the X's as dead
			_game.Play(1, 0);

			// mark all the O's as dead too
			_game.Play(1, 1);

			// restore X's and O'so
			_game.ResetDeadGroups();

			// mark all the X's as dead
			_game.Play(1, 0);

			// merge the stones marked as dead back into the board state
			_game.MergeStonesMarkedAsDead();

			Assert.AreEqual(0, _game.BlackCaptures);
			Assert.AreEqual(5, _game.WhiteCaptures);
		}

		[TestMethod]
		[ExpectedException(typeof(GoException))]
		public void TestResignThenMarkDeadStones() {
			_game.Play(0, 0);
			_game.Resign();
			_game.Play(0, 0);
		}

		[TestMethod]
		public void TestBlackResign() {
			int gameStateChangedCalled = 0;
			_game.GameStateChanged += delegate(GoGame game, GameState gameState) {
				Assert.AreEqual(GameState.BlackResigned, gameState);
				gameStateChangedCalled++;
			};

			_game.Resign();
			Assert.AreEqual(1, gameStateChangedCalled);
		}

		[TestMethod]
		public void TestWhiteResign() {
			int gameStateChangedCalled = 0;
			_game.GameStateChanged += delegate(GoGame game, GameState gameState) {
				Assert.AreEqual(GameState.WhiteResigned, gameState);
				gameStateChangedCalled++;
			};

			_game.Play(0, 0);
			_game.Resign();
			Assert.AreEqual(1, gameStateChangedCalled);
		}

		[TestMethod]
		public void TestStateOfTwoPasses() {
			int gameStateChangedCalled = 0;
			_game.GameStateChanged += delegate(GoGame game, GameState gameState) {
				if (gameStateChangedCalled == 0)
					Assert.AreEqual(GameState.WhiteToMove, gameState);
				if (gameStateChangedCalled == 1)
					Assert.AreEqual(GameState.NegotiatingDeadStones, gameState);
				if (gameStateChangedCalled > 1)
					Assert.Fail();
				gameStateChangedCalled++;
			};

			_game.Pass();
			Assert.AreEqual(1, gameStateChangedCalled);
			_game.Pass();
			Assert.AreEqual(2, gameStateChangedCalled);
		}

		/// <summary>
		///X . O . . . . . .
		///. . O . . . . . .
		///O O O . . . . . .
		///. . . . . . . . .
		///. . . . . . . . .
		///. . . . . . . . .
		///. . . . . . X X X
		///. . . . . . X . .
		///. . . . . . X . .
		/// </summary>
		[TestMethod]
		public void TestCounting() {
			_game.Play(8, 6);
			_game.Play(0, 2);
			_game.Play(7, 6);
			_game.Play(1, 2);
			_game.Play(6, 6);
			_game.Play(2, 2);
			_game.Play(6, 8);
			_game.Play(2, 1);
			_game.Play(6, 7);
			_game.Play(2, 0);
			_game.Play(0, 0);

			int pointsIdentifiedCalled = 0;

			_game.PointsIdentified += delegate(GoGame sender, BitMatrix blackPoints, BitMatrix whitePoints) {
				Assert.IsTrue(blackPoints[8, 8]);
				Assert.IsTrue(blackPoints[7, 8]);
				Assert.IsTrue(blackPoints[8, 7]);
				Assert.IsTrue(blackPoints[7, 7]);

				if (pointsIdentifiedCalled == 2 || pointsIdentifiedCalled == 3) {
					Assert.IsTrue(whitePoints[0, 0]);
					Assert.IsTrue(whitePoints[0, 1]);
					Assert.IsTrue(whitePoints[1, 0]);
					Assert.IsTrue(whitePoints[1, 1]);
				} else {
					Assert.IsFalse(whitePoints[0, 0]);
					Assert.IsFalse(whitePoints[0, 1]);
					Assert.IsFalse(whitePoints[1, 0]);
					Assert.IsFalse(whitePoints[1, 1]);
				}
				pointsIdentifiedCalled++;
			};

			AssertGameScore(4, 5.5M);

			_game.Pass();
			_game.Pass();

			AssertGameScore(4, 5.5M);

			_game.Play(0, 0);

			AssertGameScore(4, 10.5M);

			_game.MergeStonesMarkedAsDead();

			Assert.AreEqual(1, _game.WhiteCaptures);
			AssertGameScore(4, 10.5M);

			Assert.AreEqual(4, pointsIdentifiedCalled);
		}

		private void AssertGameScore(decimal expectedBlackScore, decimal expectedWhiteScore) {
			decimal actualBlackScore;
			decimal actualWhiteScore;

			_game.CalculateScore(out actualBlackScore, out actualWhiteScore);

			Assert.AreEqual(expectedBlackScore, actualBlackScore, "Actual black score does not match expected.");
			Assert.AreEqual(expectedWhiteScore, actualWhiteScore, "Actual white score does not match expected.");
		}

		/// <summary>
		///. . . . . . . . .
		///. . O . . . . . .
		///O O O . . . . . .
		///. . . . . . . . .
		///. . . . . . . . .
		///. . . . . . . . .
		///. . . . . . X X X
		///. . . . . . X . .
		///. . . . . . X . .
		/// </summary>
		[TestMethod]
		public void TestUnclosedGroup() {
			_game.Play(8, 6);
			_game.Play(0, 2);
			_game.Play(7, 6);
			_game.Play(1, 2);
			_game.Play(6, 6);
			_game.Play(2, 2);
			_game.Play(6, 8);
			_game.Play(2, 1);
			_game.Play(6, 7);

			AssertGameScore(4, 5.5M);

			_game.Pass();
			_game.Pass();

			_game.MergeStonesMarkedAsDead();

			AssertGameScore(4, 5.5M);
		}

		[TestMethod]
		public void TestLastMove() {
			Assert.IsNull(_game.LastMove);
			
			_game.Play(8, 6);

			Assert.AreEqual(8, _game.LastMove.X);
			Assert.AreEqual(6, _game.LastMove.Y);

			_game.Pass();

			Assert.IsNull(_game.LastMove);
			
			_game.Pass();

			Assert.IsNull(_game.LastMove);

			_game.MergeStonesMarkedAsDead();

			Assert.IsNull(_game.LastMove);
		}
	}
}
